/**
********************************************************************************
* @file     lv_port_fs_native.c
* @brief    对文件的简要说明
* @details  对文件的详细说明
* @author   jcai
* @date     2024-07-04 13:56:51
* @copyright Copyright &copy; 2024 wy1107412334@163.com All rights reserved
* @version  v0.0.1
*
* \n Email:  wy1107412334@163.com
* \n Blog:   None
* \n Github: https://github.com/wy1107412334
* \n Gitee:  https://gitee.com/git.git1107412334.com
*
********************************************************************************
* @note
* None.
********************************************************************************
* @attention
* None.
********************************************************************************
@verbatim
@endverbatim
********************************************************************************
* @par 历史记录
* jcai, 2024-07-04 13:56:51
* - 首次编写
*
*<center>Copyright &copy; 2024 wy1107412334@163.com All rights reserved</center>
********************************************************************************
*/

/* 头文件导入区 ------------------------------------------------------------- */

#include <stdio.h>
#include <string.h>
#include <strings.h>
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "signal_link_list.h"
#include "lv_port_fs_native.h"

#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

/** @defgroup LV_PORT_FS_NATIVE LV_PORT_FS_NATIVE
* @brief lv_port_fs_native modules
* @{
*/



/* 私有宏定义区 ------------------------------------------------------------- */

#ifndef LV_FS_NATIVE_LETTER_STR
#define LV_FS_NATIVE_LETTER_STR "N:"
#endif




/* 私有数据类型声明区 ------------------------------------------------------- */


typedef struct
{
    SL_NODE node;
    FILE *  pf;
} fil_node;
typedef struct
{
    SL_NODE node;
    DIR *   pd;
} dir_node;






/* 私有变量定义区 ----------------------------------------------------------- */

static SL_LIST list;



/** @defgroup LV_PORT_FS_NATIVE_Exported_Variables lv_port_fs_native exported variables
* @brief 全局变量定义
* @{
*/



/** defgroup LV_PORT_FS_NATIVE_Exported_Variables
* @}
*/





/* 私有函数定义区 ----------------------------------------------------------- */

/**
 * Open a file
 * @param drv       pointer to a driver where this function belongs
 * @param path      path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
 * @param mode      read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
 * @return          a file descriptor or NULL on error
 */
static void * native_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
    const char * _mode;

    void * f = NULL;
    SL_LIST * list = drv->user_data;

    if(mode == LV_FS_MODE_WR) {
        /*Open a file for write*/
        _mode = "wb";
    }
    else if(mode == LV_FS_MODE_RD) {
        /*Open a file for read*/
        _mode = "rb";
    }
    else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) {
        /*Open a file for read and write*/
        _mode = "r+b";
    }
    else
    {
        return f;
    }


    fil_node * p = malloc(sizeof(*p));
    if (p == NULL)  return f;
    bzero(p, sizeof(*p));

    p->pf = fopen(path, _mode);
    if (p->pf == NULL)
    {
        free(p);
        return f;
    }

    f = &p->node;
    sllist_insert_head(list, f);

    return f;
}

/**
 * Close an opened file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable. (opened with fs_open)
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_close(lv_fs_drv_t * drv, void * file_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    SL_LIST * list = drv->user_data;
    fil_node * p = (fil_node *) file_p;

    if (fclose(p->pf) != 0)
    {
        res = LV_FS_RES_HW_ERR;
    }
    else
    {
        res = LV_FS_RES_OK;
    }

    /*Add your code here*/
    if (sllist_delete(list, &p->node) == 0)
    {
        free(file_p);
    }

    return res;
}

/**
 * Read data from an opened file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable.
 * @param buf       pointer to a memory block where to store the read data
 * @param btr       number of Bytes To Read
 * @param br        the real number of read bytes (Byte Read)
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
    (void) drv;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    fil_node * p = (fil_node *) file_p;

    /*Add your code here*/
    size_t ret = fread(buf, 1, btr, p->pf);

    if (ferror(p->pf) == 0)
    {
        *br = ret;
        res = LV_FS_RES_OK;
    }
    else
    {
        res = LV_FS_RES_HW_ERR;
    }

    return res;
}

/**
 * Write into a file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable
 * @param buf       pointer to a buffer with the bytes to write
 * @param btw       Bytes To Write
 * @param bw        the number of real written bytes (Bytes Written). NULL if unused.
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
    (void) drv;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    fil_node * p = (fil_node *) file_p;

    size_t ret = fwrite(buf, 1, btw, p->pf);

    if (ferror(p->pf) == 0)
    {
        *bw = ret;
        res = LV_FS_RES_OK;
    }
    else
    {
        res = LV_FS_RES_HW_ERR;
    }

    return res;
}

/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable. (opened with fs_open )
 * @param pos       the new position of read write pointer
 * @param whence    tells from where to interpret the `pos`. See @lv_fs_whence_t
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
    (void) drv;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    fil_node * p = (fil_node *) file_p;
    int _whence;
    int curPtr = 0;

    switch (whence)
    {
    case LV_FS_SEEK_SET:
        _whence = SEEK_SET;
        break;
    case LV_FS_SEEK_CUR:
        _whence = SEEK_CUR;
        break;
    case LV_FS_SEEK_END:
        _whence = SEEK_END;
        break;
    default:
        curPtr = -1;
        break;
    }

    if (curPtr >= 0)
    {
        int ret = fseek(p->pf, (long)(int32_t)pos, _whence);
        if (ret == 0)
        {
            res = LV_FS_RES_OK;
        }
        else
        {
            res = LV_FS_RES_HW_ERR;
        }
    }
    else
    {
        res = LV_FS_RES_INV_PARAM;
    }

    return res;
}

/**
 * Give the position of the read write pointer
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable
 * @param pos_p     pointer to store the result
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
    (void) drv;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    fil_node * p = (fil_node *) file_p;
    *pos_p = ftell(p->pf);
    res = LV_FS_RES_OK;

    return res;
}

/**
 * Initialize a 'lv_fs_dir_t' variable for directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param path      path to a directory
 * @return          pointer to the directory read descriptor or NULL on error
 */
static void * native_dir_open(lv_fs_drv_t * drv, const char * path)
{
    void * dir = NULL;
    SL_LIST * list = (SL_LIST *) drv->user_data;

    dir_node * p = malloc(sizeof(*p));
    if (p == NULL)  return dir;

    p->pd = opendir(path);
    if (p->pd == NULL)
    {
        free(p);
        return dir;
    }

    dir = &p->node;
    sllist_insert_head(list, dir);

    return dir;
}

/**
 * Read the next filename form a directory.
 * The name of the directories will begin with '/'
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
 * @param fn        pointer to a buffer to store the filename
 * @param fn_len    length of the buffer to store the filename
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len)
{
    (void) drv;
    dir_node * p = (dir_node *) rddir_p;
    struct dirent * fni;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    int errbak = errno;

    fni = readdir(p->pd);
    if (fni == NULL)
    {
        if (errno == errbak)
        {
            *fn = '\0';
            res = LV_FS_RES_OK;
        }
        else
        {
            res = LV_FS_RES_HW_ERR;
        }
    }
    else if (strlen(fni->d_name) < fn_len)
    {
        strcpy(fn, fni->d_name);
        res = LV_FS_RES_OK;
    }
    else
    {
        res = LV_FS_RES_INV_PARAM;
    }

    return res;
}

/**
 * Close the directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t native_dir_close(lv_fs_drv_t * drv, void * rddir_p)
{
    SL_LIST * list = (SL_LIST *) drv->user_data;
    dir_node * p = (dir_node *) rddir_p;
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    int ret = closedir(p->pd);
    if (ret == 0)
    {
        res = LV_FS_RES_OK;
    }
    else
    {
        res = LV_FS_RES_HW_ERR;
    }

    if (sllist_delete(list, &p->node) == 0)
    {
        free(rddir_p);
    }

    return res;
}








/** @defgroup LV_PORT_FS_NATIVE_Exported_Functions lv_port_fs_native exported functions
* @brief 全局函数定义
* @{
*/



/**
********************************************************************************
* @brief    简要说明
* @details  详细说明
* @param    none
* @param[in]     a: 该参数用于输入
* @param[in,out] b: 该参数用于输入和输出
* @param[out]    c: 该参数用于输出
* @retval  none
* @retval  0 函数成功
* @retval  1 函数失败
* @return  返回0成功；否则失败
* @note    none
*/
void lv_port_fs_native_init(void)
{
    static lv_fs_drv_t fs_drv;
    lv_fs_drv_init(&fs_drv);

    /*Set up fields...*/
    fs_drv.letter = LV_FS_NATIVE_LETTER_STR[0];
    fs_drv.open_cb = native_open;
    fs_drv.close_cb = native_close;
    fs_drv.read_cb = native_read;
    fs_drv.write_cb = native_write;
    fs_drv.seek_cb = native_seek;
    fs_drv.tell_cb = native_tell;

    fs_drv.dir_close_cb = native_dir_close;
    fs_drv.dir_open_cb = native_dir_open;
    fs_drv.dir_read_cb = native_dir_read;

    SLLIST_INIT(&list);
    fs_drv.user_data = &list;

    lv_fs_drv_register(&fs_drv);
}


/** defgroup LV_PORT_FS_NATIVE_Exported_Functions
* @}
*/

/** defgroup LV_PORT_FS_NATIVE
* @}
*/


/*********************************** END OF FILE ******************************/
